#!/usr/bin/env bash
# Copyright (c) 2022-2024 maminjie <canpool@163.com>
# SPDX-License-Identifier: MulanPSL-2.0

CUR_DIR=$(pwd)
WORK_DIR="$CUR_DIR/build_docker"
ROOTFS_DIR="$WORK_DIR/rootfs"
YUM_DIR="$WORK_DIR/yum"
YUM_CONFIG_FILE="$YUM_DIR/yum.conf"

PRODUCT="openLinux"
VERSION="1.0"
REPOS=""
DOCKER_NAME=""
PKG_FILES=""

usage() {
printf "build docker image based on yum repository

usage:
    bash build_docker.sh [-h] [-p PRODUCT] [-v VERSION] [-r RELEASE] [-s REPOSITORY] [-f FILE]

optional arguments:
    -p PRODUCT     product name, default is openLinux
    -v VERSION     version identifier, default is 1.0
    -r RELEASE     release information, default is null
    -s REPOSITORY  source dnf repository address link (can be specified multiple times)
    -f FILE        package list file, contains extra packages (can be specified multiple times)
    -h             show the help message and exit

examples:
    bash build_docker.sh -s repo_addr
    bash build_docker.sh -p myOS -v 2024 -s repo_addr
    bash build_docker.sh -s repo_addr1 -s repo_addr2
    bash build_docker.sh -s repo_addr -f file1 -f file2
\n"
}

log_error() {
    echo "ERROR: $1"
}

parse_cmdline() {
    if [ $# -eq 0 ]; then
        usage; exit 0
    fi

    ARCH="$(uname -m)"
    if [[ "${ARCH}" != "aarch64" && "${ARCH}" != "x86_64" ]]; then
        log_error "unsupported architecture: ${ARCH}"; return 1
    fi
    local release=""
    # parse cmd line arguments
    while getopts ":p:v:r:s:f:h" opt; do
        case "$opt" in
            p)
                PRODUCT="$OPTARG"
            ;;
            v)
                VERSION="$OPTARG"
            ;;
            r)
                release="$OPTARG"
            ;;
            s)
                if [ -z "$REPOS" ]; then
                    REPOS="$OPTARG"
                else
                    REPOS="$REPOS $OPTARG"
                fi
            ;;
            f)
                if [ -f "$OPTARG" ]; then
                    if [ -z "$PKG_FILES" ]; then
                        PKG_FILES="$OPTARG"
                    else
                        PKG_FILES="$PKG_FILES $OPTARG"
                    fi
                else
                    log_error "$OPTARG is nof file"; return 1
                fi
            ;;
            h)
                usage; exit 0
            ;;
            ?)
                echo "please check the params."; usage; return 1
            ;;
        esac
    done
    if [ -z "$REPOS" ]; then
        echo "invalid REPOSITORY arguments"; return 1
    fi
    if [ -z "$release" ]; then
        DOCKER_NAME="${PRODUCT}-${VERSION}-docker-${ARCH}.tar.xz"
    else
        DOCKER_NAME="${PRODUCT}-${VERSION}-${release}-docker-${ARCH}.tar.xz"
    fi
    return 0
}

check_user() {
    if [[ $EUID -ne 0 ]]; then
        log_error "This script must be run as root!"
        exit 1
    fi
}

check_requires() {
    local ret=0

    for c in docker yum; do
        command -v $c >/dev/null
        if [ $? -ne 0 ]; then
            echo "command $c not found"
            ret=1
        fi
    done

    return $ret
}

clean() {
    cd "$CUR_DIR" || :
    [[ -d "${WORK_DIR}" ]] && rm -rf "${WORK_DIR}"
}

# create_yum_repo YUM_DIR YUM_CONFIG_FILE REPOS
create_yum_repo() {
    local yum_dir="$1"
    local yum_config_file="$2"
    local repos="$3"
    local yum_repo_dir="${yum_dir}/yum.repos.d"
    local yum_repo_file="${yum_repo_dir}/os.repo"

    mkdir -p "$yum_repo_dir"

    cat > "${yum_config_file}" <<EOF
[main]
gpgcheck=1
installonly_limit=3
clean_requirements_on_remove=True
best=True
skip_if_unavailable=False
reposdir=${yum_repo_dir}
EOF

    local i=1
    for repo in $repos; do
        cat >> "${yum_repo_file}" <<EOF
[os_repo_$i]
name=$repo
baseurl=$repo
enabled=1
gpgcheck=0

EOF
        ((i++))
    done
}

do_main() {
    clean

    mkdir -p "$YUM_DIR"
    create_yum_repo "$YUM_DIR" "$YUM_CONFIG_FILE" "$REPOS"

    mkdir -p "$ROOTFS_DIR"
    yum install -c "$YUM_CONFIG_FILE" --installroot="$ROOTFS_DIR" --setopt=install_weak_deps=False -y dnf procps-ng rootfiles tar vim-minimal
    if [ $? -ne 0 ]; then
        log_error "yum install basic packages failed"; return 1
    fi
    for f in $PKG_FILES; do
        yum install -c "$YUM_CONFIG_FILE" --installroot="$ROOTFS_DIR" --setopt=install_weak_deps=False -y $(tr '\n' ' ' < "$f")
        if [ $? -ne 0 ]; then
            log_error "yum install customize packages failed"; return 1
        fi
    done
    # TODO: fine cut
    cd "$ROOTFS_DIR" || :
    rm -rf usr/share/{man,doc,locale,info,mime}/*
    rm -rf var/cache/{yum,dnf}/*
    rm -rf var/lib/{yum,dnf}/*

    cd "$WORK_DIR" || :

    local rootfs_relpath=$(realpath --relative-to=$WORK_DIR $ROOTFS_DIR)
    local docker_tag="$(echo "$PRODUCT:$VERSION" | tr A-Z a-z)"
    cat > ./dockerfile << EOF
FROM scratch
COPY $rootfs_relpath /
CMD ["/bin/bash"]
EOF

    docker build -t $docker_tag .
    if [ $? -ne 0 ]; then
        log_error "docker build failed"; return 1
    fi
    echo "Waiting for archiving container image..."
    docker save $docker_tag | xz -z -c -9 > $CUR_DIR/$DOCKER_NAME
    if [ $? -ne 0 ]; then
        log_error "docker save failed"; return 1
    fi
    docker run --rm $docker_tag /bin/bash -c 'cat /etc/os-release'
    if [ $? -ne 0 ]; then
        log_error "docker run failed"; return 1
    fi
    docker rmi $docker_tag
    if [ $? -ne 0 ]; then
        log_error "docker rmi failed"; return 1
    fi

    clean

    return 0
}

main() {
    check_user

    parse_cmdline "$@"
    if [ $? -ne 0 ]; then
        log_error "parse params failed, try -h for help"
        return 1
    fi

    check_requires
    if [ $? -ne 0 ]; then
        log_error "check requires failed"
        return 1
    fi

    do_main
    if [ $? -ne 0 ]; then
        return 1
    fi

    return 0
}

main "$@"
